package com.binance.gateway.filter.auth;

import com.binance.gateway.app.base.helper.SysConfigHelper;
import com.binance.gateway.app.constant.Constant;
import com.binance.gateway.constant.GwFilterOrders;
import com.binance.gateway.util.CallBackUtil;
import com.binance.gateway.util.FilterUtil;
import com.binance.gateway.util.UserAgentUtil;
import com.binance.master.utils.StringUtils;
import com.binance.master.utils.WebUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

@Component
@Slf4j
public class IllegalRequestFilter extends ZuulFilter {

    @Autowired
    private SysConfigHelper sysConfigHelper;

    @Value("${callback.domain.white.list}")
    private String callbackDomainWhiteList;

    @Value("${user.agent.filter.on:true}")
    private boolean userAgentFilterOn;

    private static final String INTERCEPT_SWITCH = "intercept_switch";
    private static final String INTERCEPT_URL_IGNORE = "intercept_url_Ignore";
    private static final String INTERCEPT_PARAMS_IGNORE = "intercept_params_Ignore";
    private static final String INTERCEPT_WORDS = "intercept_words";
    private static final String INTERCEPT_URLS = "intercept_urls";

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return GwFilterOrders.ILLEGAL_REQUEST;
    }

    @Override
    public boolean shouldFilter() {
        String filterSwitch = sysConfigHelper.getCodeByDisplayName(INTERCEPT_SWITCH);
        if (StringUtils.isEmpty(filterSwitch) || !"on".endsWith(filterSwitch.toLowerCase())) {
            return false;
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        final RequestContext ctx = RequestContext.getCurrentContext();
        final HttpServletRequest request = ctx.getRequest();
        final HttpServletResponse response = ctx.getResponse();
        String requestUrl = request.getRequestURL().toString();

        // -1.拦截非法UserAgent
        if (userAgentFilterOn && !requestUrl.contains("/wapi/")) {
            String clientType = request.getHeader(Constant.HEADER_CLIENT_TYPE);
            String userAgent = request.getHeader(Constant.USER_AGENT);
            if (StringUtils.isNotBlank(clientType) && UserAgentUtil.illegalUserAgent(clientType.toLowerCase(), userAgent)) {
                log.warn("illegalUserAgent userIp:{}, clientType:{}, userAgent:{}, method:{}, requestUrl:{}",
                        WebUtils.getRequestIp(), clientType, userAgent, request.getMethod(), request.getRequestURL().toString());
                FilterUtil.badRequest(response);
                return null;
            }
        }

        // 0.拦截非法callback
        String callbackUrl = request.getParameter("callback");
        if (StringUtils.isBlank(callbackUrl)) {
            callbackUrl = request.getParameter("return_to");
        }
        if (!StringUtils.isBlank(callbackUrl)) {
            CallBackUtil.checkCallbackUrl(callbackUrl, response, callbackDomainWhiteList);
        }

        // 1.忽略的url
        String ignoreUrls = sysConfigHelper.getCodeByDisplayName(INTERCEPT_URL_IGNORE);
        if (StringUtils.isNotBlank(ignoreUrls)) {
            String[] urls = ignoreUrls.split(",");
            for (String url : urls) {
                if (StringUtils.isNotBlank(url) && requestUrl.contains(url)) {
                    return null;
                }
            }
        }

        // 2.拦截的url
        String badUrlsStr = sysConfigHelper.getCodeByDisplayName(INTERCEPT_URLS);
        String ipAddress = WebUtils.getRequestIp();
        if (ipAddress == null) {
            ipAddress = request.getRemoteAddr();
        }
        String[] ips = ipAddress.split(",");
        if (ips.length > 1) {
            ipAddress = ips[0].trim();
        }
        if (StringUtils.isNotBlank(badUrlsStr)) {
            String[] urls = badUrlsStr.split(",");
            for (String url : urls) {
                if (StringUtils.isNotBlank(url) && requestUrl.contains(url)) {
                    log.warn(
                            String.format("请求URL拦截--IP:%s; request:%s; interceptUrl:%s", ipAddress, requestUrl, url));
                    FilterUtil.badRequest(response);
                }
            }
        }

        // 3.请求参数忽略&拦截关键字
        String ignoreParams = sysConfigHelper.getCodeByDisplayName(INTERCEPT_PARAMS_IGNORE);
        Map<String, String[]> paramMap = request.getParameterMap();
        for (String key : paramMap.keySet()) {
            if (ignoreParams.contains(key))
                continue;
            String[] values = paramMap.get(key);
            for (String value : values) {
                String badStr = wordsValidate(value);
                if (StringUtils.isNotBlank(badStr)) {
                    log.warn(String.format(
                            "非法请求关键字拦截--IP:%s; request:%s; interceptParam:%s; interceptValue:%s; interceptWords:%s",
                            ipAddress, requestUrl, key, value, badStr));
                    FilterUtil.badRequest(response);
                }
            }
        }

        return null;
    }

    private String wordsValidate(String value) {
        String badWordsStr = sysConfigHelper.getCodeByDisplayName(INTERCEPT_WORDS);
        if (StringUtils.isBlank(badWordsStr))
            return "";
        StringBuilder sb = new StringBuilder();
        String[] badWords = badWordsStr.toLowerCase().split("\\|");
        value = value.toLowerCase();
        for (String badWord : badWords) {
            if (StringUtils.isNotBlank(badWord) && value.contains(badWord)) {
                sb.append(badWord).append(",");
            }
        }
        return sb.toString();
    }

}
